/*
 * Created on 2005-10-17
 */
package org.opencube.util.services.logging;

import info.fingo.util.Text;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.opencube.data.BoundVariable;

public class StatementLogger
{
	private static boolean initialized = false;
	private static String loggerPath = null;
	private static String application = null;
	private static String hostname = Text.null2string( System.getProperty( "HOSTNAME" ) );
	
	private static final String LANGUAGE = "java";
	private static final String EMPTY_VALUE = "\"\";";
	private static final String SEMICOLON = ";";
	private static final String COMA = ",";
	private static final String QUOTATION = "\"";
	private static final String NEW_LINE = "\n";
	private static final String LEFT_BRACKET = "[";
	private static final String RIGHT_BRACKET = "]";
	private static final String UNDERSCORE = "_";
	
	private static final String SEPARATOR = SEMICOLON;
	private static final String VARIABLE_SEPARATOR = COMA;
	private static final String VARIABLE_SET_SEPARATOR = COMA;
	
	private static SimpleDateFormat dateFormatter = new SimpleDateFormat( "yyyyMMdd" );
	private static SimpleDateFormat timeFormatter = new SimpleDateFormat( "HH:mm:ss" );
	private static DecimalFormat decimalFormatter = new DecimalFormat( "0.0################" ); 
	
	public static void init( String applicationName, String path ) throws IOException
	{
		if( !initialized )
		{
			if( path == null )
			{
				throw new IllegalArgumentException( "Path cannot be null" );
			}
			if( applicationName == null )
			{
				throw new IllegalArgumentException( "Application name cannot be null" );
			}
			application = applicationName;
			File f = new File( path );
			f.getParentFile().mkdirs();
			if( hostname.length() > 0 )
			{
				f = new File( f.getParent(), hostname + UNDERSCORE + f.getName() );
			}
			loggerPath = f.getPath();
			if( !f.exists() || f.length() == 0 )
			{
				writeHeader();
			}
			initialized = true;
		}
	}
	
	public static final void logStatement( String namespace, String resourceKey, String statement, BoundVariable[] variables, long executionTime, long parseTime )
	{
		logStatement( namespace, resourceKey, statement, variables, executionTime, parseTime, 0 );
	}
	
	public static final void logStatement( String namespace, String resourceKey, String statement, BoundVariable[] variables, long executionTime, long parseTime, long fetchTime )
	{
		log( false, namespace, resourceKey, statement, variables, null, executionTime, parseTime, fetchTime );
	}
	
	public static final void logBatchStatement( String namespace, String resourceKey, String statement, BoundVariable[][] variables, long executionTime, long parseTime )
	{
		log( true, namespace, resourceKey, statement, null, variables, executionTime, parseTime, 0 );
	}
	
	private static void log( boolean batch, String namespace, String resourceKey, String statement, BoundVariable[] vars, BoundVariable[][] batchVars, long executionTime, long parseTime, long fetchTime )
	{
		try
		{
			if( !initialized )
			{
				return;
			}
			
			Date now = new Date();
			StringBuffer line = new StringBuffer();
			line.append( NEW_LINE );
			// l_language
			line.append( prepare( LANGUAGE ) );
			// l_date
			line.append( prepare( getDate( now ) ) );
			// l_cach_key
			line.append( EMPTY_VALUE );
			// l_host
			StringBuffer host = new StringBuffer();
			host.append( Text.null2string( hostname ) );
			host.append( "(" );
			host.append( application );
			host.append( ")" );
			line.append( prepare( host.toString() ) );
			// l_query
			line.append( EMPTY_VALUE );
			// l_time
			line.append( prepare( getTime( now ) ) );
			// l_parsing_query
			line.append( prepare( getSeconds( parseTime ) ) );
			// l_executing_query
			line.append( prepare( getSeconds( executionTime ) ) );
			// l_fetchhing_data
			line.append( prepare( getSeconds( fetchTime ) ) );
			// l_res_key
			line.append( prepare( resourceKey ) );
			// l_res_namespace
			line.append( prepare( namespace ) );
			// l_bindvariables
			if( batch )
			{
				line.append( prepare( join( batchVars) ) );
			}
			else
			{
				line.append( prepare( join( vars ) ) );
			}
			// l_params
			line.append( EMPTY_VALUE );
			
			write( line.toString() );
		}
		catch( Throwable t )
		{
			t.printStackTrace();
		}
	}
	
	private static String getDate( Date date )
	{
		return dateFormatter.format( date );
	}
		
	private static String getTime( Date date )
	{
		return timeFormatter.format( date );
	}
	
	private static String getVariableString( BoundVariable bv )
	{
		StringBuffer result = new StringBuffer( bv.getName() );
		result.append( "=" ); 
		result.append( bv.getValue() ); 
		return result.toString();
	}
	
	private static String join( BoundVariable[] data )
	{
		StringBuffer result = new StringBuffer();
		if( data != null && data.length > 0 )
		{
			result.append( getVariableString( data[ 0 ] ) );
			for( int i = 1; i < data.length; i++ )
			{
				result.append( VARIABLE_SEPARATOR );
				result.append( getVariableString( data[ i ] ) );
			}
		}
		return result.toString();
	}
	
	private static String join( BoundVariable[][] data )
	{
		StringBuffer result = new StringBuffer();
		if( data != null && data.length > 0 )
		{
			result.append( wrap( join( data[ 0 ] ), LEFT_BRACKET, RIGHT_BRACKET ) );
			for( int i = 1; i < data.length; i++ )
			{
				result.append( VARIABLE_SET_SEPARATOR );
				result.append( wrap( join( data[ i ] ), LEFT_BRACKET, RIGHT_BRACKET ) );
			}
		}
		return result.toString();
	}
	
	private static String wrap( String value, String leftDelimiter, String rightDelimiter )
	{
		StringBuffer sb = new StringBuffer();
		sb.append( leftDelimiter );
		sb.append( value );
		sb.append( rightDelimiter );
		return sb.toString();
	}
	
	private static String prepare( String s )
	{
		StringBuffer sb = new StringBuffer();
		sb.append( wrap( escape( s ), QUOTATION, QUOTATION ) );
		sb.append( SEPARATOR );
		return sb.toString();
	}
	
	private static void writeHeader() throws IOException
	{
		String headerLine = "\"l_language\";\"l_date\";\"l_cach_key\";\"l_host\";\"l_query\";\"l_time\";\"l_parsing_query\";\"l_executing_query\";\"l_fetchhing_data\";\"l_res_key\";\"l_res_namespace\";\"l_bindvariables\";\"l_params\";";
		write( headerLine );
	}
	
	private static void write( String line ) throws IOException
	{
		BufferedWriter out = new BufferedWriter( new FileWriter( loggerPath, true ) );
		out.write( line );
		out.close();
	}
	
	public static void main( String[] args )
	{
		try
		{
			StatementLogger.init( "statement logger test", "c:/tmp/a/b/cd/file.txt" );
			StatementLogger.logStatement( "default_namespace", "select_dual.res", "select * from dual", new BoundVariable[]{ new BoundVariable( "bv1", "abcd" ), new BoundVariable( "bv2", null ) }, 150, 300, 15 );
		}
		catch( Exception e )
		{
			e.printStackTrace();
		}
	}
	
	private static String escape( String o )
	{
		String s = o == null ? "" : o;
		StringBuffer result = new StringBuffer();
		for( int i = 0; s != null && i < s.length(); i++ )
		{
			char c = s.charAt( i ); 
			if( c == '"' )
			{
				result.append( "\"\"" );
			}
			else if( c == '\n' )
			{
				result.append( " " );
			}
			else if( c == '\t' )
			{
				result.append( " " );
			}
			else if( c == '\r' )
			{
				result.append( " " );
			}
			else
			{
				result.append( c );
			}
				
		}
		return result.toString();
	}
	
	private static String getSeconds( long nano )
	{
		return decimalFormatter.format( (double) nano / 1000000000 );
	}
}
